<script lang="tsx">
   import type { Ref } from 'vue';
   import { defineComponent, ref, computed, unref, reactive, watch, watchEffect } from 'vue';
   import { useTimeoutFn } from '/@/hooks/core/useTimeout';
   import { useEventListener } from '/@/hooks/event/useEventListener';
   import { basicProps } from './props';
   import { getSlot } from '/@/utils/helper/tsxHelper';
   import { CheckOutlined, DoubleRightOutlined } from '@ant-design/icons-vue';

   export default defineComponent({
      name: 'BaseDargVerify',
      props: basicProps,
      emits: ['success', 'update:value', 'change', 'start', 'move', 'end'],
      setup(props, { emit, slots, expose }) {
         const state = reactive({
            isMoving: false,
            isPassing: false,
            moveDistance: 0,
            toLeft: false,
            startTime: 0,
            endTime: 0,
         });

         const wrapElRef = ref<HTMLDivElement | null>(null);
         const barElRef = ref<HTMLDivElement | null>(null);
         const contentElRef = ref<HTMLDivElement | null>(null);
         const actionElRef = ref(null) as Ref<HTMLDivElement | null>;

         useEventListener({
            el: document,
            name: 'mouseup',
            listener: () => {
               if (state.isMoving) {
                  resume();
               }
            },
         });

         const getActionStyleRef = computed(() => {
            const { height, actionStyle } = props;
            const h = `${parseInt(height as string)}px`;
            return {
               left: 0,
               width: h,
               height: h,
               ...actionStyle,
            };
         });

         const getWrapStyleRef = computed(() => {
            const { height, width, circle, wrapStyle } = props;
            const h = parseInt(height as string);
            const w = `${parseInt(width as string)}px`;
            return {
               width: w,
               height: `${h}px`,
               lineHeight: `${h}px`,
               borderRadius: circle ? h / 2 + 'px' : 0,
               ...wrapStyle,
            };
         });

         const getBarStyleRef = computed(() => {
            const { height, circle, barStyle } = props;
            const h = parseInt(height as string);
            return {
               height: `${h}px`,
               borderRadius: circle ? h / 2 + 'px 0 0 ' + h / 2 + 'px' : 0,
               ...barStyle,
            };
         });

         const getContentStyleRef = computed(() => {
            const { height, width, contentStyle } = props;
            const h = `${parseInt(height as string)}px`;
            const w = `${parseInt(width as string)}px`;

            return {
               height: h,
               width: w,
               ...contentStyle,
            };
         });

         watch(
            () => state.isPassing,
            (isPassing) => {
               if (isPassing) {
                  const { startTime, endTime } = state;
                  const time = (endTime - startTime) / 1000;
                  emit('success', { isPassing, time: time.toFixed(1) });
                  emit('update:value', isPassing);
                  emit('change', isPassing);
               }
            },
         );

         watchEffect(() => {
            state.isPassing = !!props.value;
         });

         function getEventPageX(e: MouseEvent | TouchEvent) {
            return (e as MouseEvent).pageX || (e as TouchEvent).touches[0].pageX;
         }

         function handleDragStart(e: MouseEvent | TouchEvent) {
            if (state.isPassing) {
               return;
            }
            const actionEl = unref(actionElRef);
            if (!actionEl) return;
            emit('start', e);
            state.moveDistance =
               getEventPageX(e) - parseInt(actionEl.style.left.replace('px', ''), 10);
            state.startTime = new Date().getTime();
            state.isMoving = true;
         }

         function getOffset(el: HTMLDivElement) {
            const actionWidth = parseInt(el.style.width);
            const { width } = props;
            const widthNum = parseInt(width as string);
            const offset = widthNum - actionWidth - 6;
            return { offset, widthNum, actionWidth };
         }

         function handleDragMoving(e: MouseEvent | TouchEvent) {
            const { isMoving, moveDistance } = state;
            if (isMoving) {
               const actionEl = unref(actionElRef);
               const barEl = unref(barElRef);
               if (!actionEl || !barEl) return;
               const { offset, widthNum, actionWidth } = getOffset(actionEl);
               const moveX = getEventPageX(e) - moveDistance;

               emit('move', {
                  event: e,
                  moveDistance,
                  moveX,
               });
               if (moveX > 0 && moveX <= offset) {
                  actionEl.style.left = `${moveX}px`;
                  barEl.style.width = `${moveX + actionWidth / 2}px`;
               } else if (moveX > offset) {
                  actionEl.style.left = `${widthNum - actionWidth}px`;
                  barEl.style.width = `${widthNum - actionWidth / 2}px`;
                  if (!props.isSlot) {
                     checkPass();
                  }
               }
            }
         }

         function handleDragOver(e: MouseEvent | TouchEvent) {
            const { isMoving, isPassing, moveDistance } = state;
            if (isMoving && !isPassing) {
               emit('end', e);
               const actionEl = unref(actionElRef);
               const barEl = unref(barElRef);
               if (!actionEl || !barEl) return;
               const moveX = getEventPageX(e) - moveDistance;
               const { offset, widthNum, actionWidth } = getOffset(actionEl);
               if (moveX < offset) {
                  if (!props.isSlot) {
                     resume();
                  } else {
                     setTimeout(() => {
                        if (!props.value) {
                           resume();
                        } else {
                           const contentEl = unref(contentElRef);
                           if (contentEl) {
                              contentEl.style.width = `${parseInt(barEl.style.width)}px`;
                           }
                        }
                     }, 0);
                  }
               } else {
                  actionEl.style.left = `${widthNum - actionWidth}px`;
                  barEl.style.width = `${widthNum - actionWidth / 2}px`;
                  checkPass();
               }
               state.isMoving = false;
            }
         }

         function checkPass() {
            if (props.isSlot) {
               resume();
               return;
            }
            state.endTime = new Date().getTime();
            state.isPassing = true;
            state.isMoving = false;
         }

         function resume() {
            state.isMoving = false;
            state.isPassing = false;
            state.moveDistance = 0;
            state.toLeft = false;
            state.startTime = 0;
            state.endTime = 0;
            const actionEl = unref(actionElRef);
            const barEl = unref(barElRef);
            const contentEl = unref(contentElRef);
            if (!actionEl || !barEl || !contentEl) return;
            state.toLeft = true;
            useTimeoutFn(() => {
               state.toLeft = false;
               actionEl.style.left = '0';
               barEl.style.width = '0';
               //  The time is consistent with the animation time
            }, 300);
            contentEl.style.width = unref(getContentStyleRef).width;
         }

         expose({
            resume,
         });

         return () => {
            const renderBar = () => {
               const cls = [`darg-verify-bar`];
               if (state.toLeft) {
                  cls.push('to-left');
               }
               return <div class={cls} ref={barElRef} style={unref(getBarStyleRef)} />;
            };

            const renderContent = () => {
               const cls = [`darg-verify-content`];
               const { isPassing } = state;
               const { text, successText } = props;

               isPassing && cls.push('success');

               return (
                  <div class={cls} ref={contentElRef} style={unref(getContentStyleRef)}>
                     {getSlot(slots, 'text', isPassing) || (isPassing ? successText : text)}
                  </div>
               );
            };

            const renderAction = () => {
               const cls = [`darg-verify-action`];
               const { toLeft, isPassing } = state;
               if (toLeft) {
                  cls.push('to-left');
               }
               return (
                  <div
                     class={cls}
                     onMousedown={handleDragStart}
                     onTouchstart={handleDragStart}
                     style={unref(getActionStyleRef)}
                     ref={actionElRef}
                  >
                     {getSlot(slots, 'actionIcon', isPassing) ||
                        (isPassing ? (
                           <CheckOutlined class={`darg-verify-action__icon`} />
                        ) : (
                           <DoubleRightOutlined class={`darg-verify-action__icon`} />
                        ))}
                  </div>
               );
            };

            return (
               <div
                  class="darg-verify"
                  ref={wrapElRef}
                  style={unref(getWrapStyleRef)}
                  onMousemove={handleDragMoving}
                  onTouchmove={handleDragMoving}
                  onMouseleave={handleDragOver}
                  onMouseup={handleDragOver}
                  onTouchend={handleDragOver}
               >
                  {renderBar()}
                  {renderContent()}
                  {renderAction()}
               </div>
            );
         };
      },
   });
</script>
<style lang="less">
   @radius: 4px;

   .darg-verify {
      position: relative;
      overflow: hidden;
      text-align: center;
      background-color: rgb(238 238 238);
      border: 1px solid #ddd;
      border-radius: @radius;

      &-bar {
         position: absolute;
         width: 0;
         height: 36px;
         background-color: @success-color;
         border-radius: @radius;

         &.to-left {
            width: 0 !important;
            transition: width 0.3s;
         }
      }

      &-content {
         position: absolute;
         top: 0;
         font-size: 12px;
         text-size-adjust: none;
         background-color: -webkit-gradient(
            linear,
            left top,
            right top,
            color-stop(0, #333),
            color-stop(0.4, #333),
            color-stop(0.5, #fff),
            color-stop(0.6, #333),
            color-stop(1, #333)
         );
         animation: slidetounlock 3s infinite;
         background-clip: text;
         user-select: none;

         &.success {
            -webkit-text-fill-color: @white;
         }

         & > * {
            -webkit-text-fill-color: #333;
         }
      }

      &-action {
         position: absolute;
         top: 0;
         left: 0;
         display: flex;
         cursor: move;
         background-color: @white;
         border-radius: @radius;
         justify-content: center;
         align-items: center;

         &__icon {
            cursor: inherit;
         }

         &.to-left {
            left: 0 !important;
            transition: left 0.3s;
         }
      }
   }

   @keyframes slidetounlock {
      0% {
         background-position: -120px 0;
      }

      100% {
         background-position: 120px 0;
      }
   }
</style>
